<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Test Behavior During Timer Flood</title>
  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();

function onLoad() {
  return new Promise(resolve => {
    addEventListener('load', resolve, { once: true });
  });
}

// Create a frame that executes a timer flood.  The frame signals
// that is ready once the flood has had a chance to warm up.
function withFloodFrame() {
  return new Promise(resolve => {
    let frame = document.createElement('iframe');
    addEventListener('message', function onMsg(evt) {
      if (evt.data === 'STARTED') {
        removeEventListener('message', onMsg);
        resolve(frame);
      }
    });
    frame.src = 'file_timer_flood.html';
    document.body.appendChild(frame);
  });
}

// Test that we can load documents during a timer flood.
function testFrameLoad() {
  return new Promise(resolve => {
    let frame = document.createElement('iframe');
    frame.addEventListener('load', _ => {
      frame.remove();
      resolve();
    }, { once: true });
    document.body.appendChild(frame);
  });
}

// Test that we can perform network requests while a timer flood
// is occuring.
function testFetch(url) {
  return fetch(url).then(response => {
    return response.text();
  });
}

// Test that we can run animations for 5 seconds while a timer
// flood is occuring.
function testRequestAnimationFrame() {
  return new Promise(resolve => {
    let remainingFrames = 5 * 60;
    function nextFrame() {
      remainingFrames -= 1;
      if (remainingFrames > 0) {
        requestAnimationFrame(nextFrame);
      } else {
        resolve();
      }
    };
    requestAnimationFrame(nextFrame);
  });
}

let floodFrame;

onLoad().then(_ => {
  // Start a timer flood in a frame.
  return withFloodFrame();
}).then(frame => {
  floodFrame = frame;

  // Next we are going to start a bunch of asynchronous work that we
  // expect to complete in spite of the timer flood.  The type of work
  // is a bit arbitrary, but is chosen to reflect the kinds of things
  // we would like the browser to be able to do even when pages are
  // abusing timers.  Feel free to add more types of work here, but
  // think carefully before removing anything.
  let tests = [];

  // Verify we can perform a variety of work while the timer flood
  // is running.
  for (let i = 0; i < 20; ++i) {
    tests.push(testFrameLoad());
    tests.push(testFetch('file_timer_flood.html'));
  }
  // Verify that animations still work while the timer flood is running.
  // Note that we do one long run of animations instead of parallel runs
  // like the other activities because of the way requestAnimationFrame()
  // is scheduled.  Parallel animations would not result in any additional
  // runnables be placed on the event queue.
  tests.push(testRequestAnimationFrame());

  // Wait for all tests to finish.  If we do not handle the timer flood
  // well then this will likely time out.
  return Promise.all(tests);
}).then(_ => {
  ok(true, 'completed tests without timing out');
  floodFrame.remove();
  SimpleTest.finish();
});
</script>
</pre>
</body>
</html>
